package Simcomp::CPU;
use strict;
use warnings;
use lib qw( ../ );
use Simcomp::Process;
use Simcomp::Result;

our $VERSION = "0.01";

sub new
{
	my $class = shift;
	my %attr = @_;
	
	$attr{'current_time'} = 0;
	$attr{'event_time'} = undef;
	$attr{'state'} = "idle";
	$attr{'queue'} = [()];
	bless \%attr, $class;
	return \%attr;
	
}

sub disk_A
{
	$_[0]->{'disk_A'};
}

sub disk_B
{
	$_[0]->{'disk_B'};
}

sub set_state
{
	$_[0]->{'state'} = $_[1];
}

sub result
{
	$_[0]->{'result'};
}

sub state
{
	$_[0]->{'state'};
}

sub set_event_time
{
	$_[0]->{'event_time'} = $_[1];
}

sub event_time
{
	$_[0]->{'event_time'};
}

sub set_current_time
{
	$_[0]->{'current_time'} = $_[1];
}

sub current_time
{
	$_[0]->{'current_time'};
}

sub set_current_process
{
	$_[0]->{'current_process'} = $_[1];
}

sub current_process
{
	$_[0]->{'current_process'};
}

sub add_to_queue
{
	my $self=shift;
	push @{$self->{'queue'}}, shift;
}

sub remove_from_queue
{
	my $self = shift;
	shift @{$self->{'queue'}};
}

sub receive_process
{
    my $self = shift;
    my $process = shift;
    my $cputime_sub = Simcomp::Random::getDistribution( $self->{'process_time'}->{'type'} );
    my $quantum_sub = Simcomp::Random::getDistribution( $self->{'quantum'}->{'type'} );
    my $quantum = &$quantum_sub( @{$self->{'quantum'}->{'params'}} );
    $process->set_time_in_cpu( &$cputime_sub( @{$self->{'process_time'}->{'params'}} ) );
    $process->set_duration( $process->time_in_cpu );
    if ( $self->state eq "idle" )
    {
		$self->set_state( "processing" );
		$self->set_current_process( $process );
		if ($process->duration > $quantum)
		{
			$process->set_duration($process->duration - $quantum);
			$self->set_event_time( $self->current_time + $quantum );
	
			
		}
		elsif ($process->duration <= $quantum)
		{
			$self->set_event_time( $self->current_time + $process->duration );
			$process->set_duration(0);
		}
    }
    elsif ( ( $self->state eq "processing" ) or ($self->state eq "overhead" ) )
    {
		$self->add_to_queue( $process );
    }
}

sub act
{
	my $self = shift;
    if ($self->state eq 'overhead')
    {
    	if (scalar @{ $self->{'queue'} } )
    	{
    		$self->set_state('processing');
    		my $process = $self->remove_from_queue;
    		$self->set_current_process( $process );
    		my $quantum_sub = Simcomp::Random::getDistribution( $self->{'quantum'}->{'type'} );
   			my $quantum = &$quantum_sub( @{$self->{'quantum'}->{'params'}} );
    		if ($process->duration > $quantum)
		{
		    $process->set_duration($process->duration - $quantum);
		    $self->set_event_time( $self->current_time + $quantum );
		}
		elsif ($process->duration <= $quantum)
		{
		    $self->set_event_time( $self->current_time + $process->duration );
		    $process->set_duration(0);
		}
    	}
    	else
    	{
    		$self->set_state('idle');
		$self->set_event_time( undef );
    	}
    }
    elsif ($self->state eq 'processing')
    {
    	my $process = $self->current_process;
    	if($process->duration > 0)
    	{
    		$self->add_to_queue($process);
    		$self->set_state('overhead');
    		my $overhead_sub = Simcomp::Random::getDistribution( $self->{'overhead'}->{'type'} );
    		my $overhead = &$overhead_sub( @{$self->{'overhead'}->{'params'}} );
    		$self->set_event_time($self->current_time + $overhead);
    
    	}
    	elsif ($process->duration == 0)
    	{
    		my $random = rand(1);
    		my $disk;
    		if ($random > 0.2)
    		{
    			$disk = $self->disk_B;
    			$process->set_disk("DISK_B")
    		}
    		else
    		{
    			$disk = $self->disk_A;
    			$process->set_disk("DISK_A")
    		}
    		$process->set_time_out_cpu($self->current_time);
    		$disk->receive_process($process);
    		$self->set_state('overhead');
    		my $overhead_sub = Simcomp::Random::getDistribution( $self->{'overhead'}->{'type'} );
    		my $overhead = &$overhead_sub( @{$self->{'overhead'}->{'params'}} );
    		$self->set_event_time($self->current_time + $overhead);
    	}
    }
}

sub feed_result
{
	my $self = shift;
	my $result = $self->result;
	my $queue_size = scalar @{ $self->{'queue'} };
	$result->add_queue_size_in_cpu($queue_size, $self->current_time);
	$result->add_cpu_state($self->state, $self->current_time);
}

1;
